2 * wiggle - apply rejected patches
4 * Copyright (C) 2005 Neil Brown <neilb@cse.unsw.edu.au>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * Email: <neilb@suse.de>
26 * vpatch - visual front end for wiggle
28 * "files" display, lists all files with statistics
29 * - can hide various lines including subdirectories
30 * and files without wiggles or conflicts
31 * "diff" display shows merged file with different parts
32 * in different colours
33 * - untouched are pale A_DIM
34 * - matched/remaining are regular A_NORMAL
35 * - matched/removed are red/underlined A_UNDERLINE
36 * - unmatched in file are A_STANDOUT
37 * - unmatched in patch are A_STANDOUT|A_UNDERLINE ???
38 * - inserted are inverse/green ?? A_REVERSE
40 * The window can be split horiz or vert and two different
41 * views displayed. They will have different parts missing
43 * So a display of NORMAL, underline, standout|underline reverse
44 * should show a normal patch.
57 #define assert(x) do { if (!(x)) abort(); } while (0)
61 unsigned int start, end;
65 int chunks, wiggles, conflicts;
69 struct plist *patch_add_file(struct plist *pl, int *np, char *file,
70 unsigned int start, unsigned int end)
72 /* size of pl is 0, 16, n^2 */
76 while (*file == '/') file++; /* leading '/' are bad... */
78 /* printf("adding %s at %d: %u %u\n", file, n, start, end); */
80 else if (n<=16) asize = 16;
81 else if ((n&(n-1))==0) asize = n;
82 else asize = n+1; /* not accurate, but not too large */
84 /* need to extend array */
86 if (asize < 16) asize = 16;
88 npl = realloc(pl, asize * sizeof(struct plist));
90 fprintf(stderr, "malloc failed - skipping %s\n", file);
98 pl[n].last = pl[n].next = pl[n].prev = pl[n].parent = -1;
99 pl[n].chunks = pl[n].wiggles = 0; pl[n].conflicts = 100;
108 struct plist *parse_patch(FILE *f, FILE *of, int *np)
110 /* read a multi-file patch from 'f' and record relevant
111 * details in a plist.
112 * if 'of' >= 0, fd might not be seekable so we write
113 * to 'of' and use lseek on 'of' to determine position
115 struct plist *plist = NULL;
118 /* first, find the start of a patch: "\n+++ "
119 * grab the file name and scan to the end of a line
121 char *target="\n+++ ";
122 char *target2="\n--- ";
128 while (*pos && (c=fgetc(f)) != EOF ) {
129 if (of) fputc(c, of);
137 /* now read a file name */
139 while ((c=fgetc(f)) != EOF && c != '\t' && c != '\n' && c != ' ' &&
142 if (of) fputc(c, of);
147 if (of) fputc(c, of);
148 while (c != '\n' && (c=fgetc(f)) != EOF) {
149 if (of) fputc(c, of);
151 start = of ? ftell(of) : ftell(f);
155 /* now skip to end - "\n--- " */
158 while (*pos && (c=fgetc(f)) != EOF) {
159 if (of) fputc(c, of);
164 end = of ? ftell(of) : ftell(f);
166 end -= (pos - target2) - 1;
167 plist = patch_add_file(plist, np,
168 strdup(name), start, end);
175 fprintf(stderr,"vpatch: fatal error\n");
181 static struct stream load_segment(FILE *f,
182 unsigned int start, unsigned int end)
186 s.body = malloc(s.len);
189 if (fread(s.body, 1, s.len, f) != s.len) {
205 nocbreak();nl();endwin();
206 printf("Died on signal %d\n", sig);
210 int pl_cmp(const void *av, const void *bv)
212 const struct plist *a = av;
213 const struct plist *b = bv;
214 return strcmp(a->file, b->file);
217 int common_depth(char *a, char *b)
219 /* find number of patch segments that these two have
227 if (c) al = c-a; else al = strlen(a);
229 if (c) bl = c-b; else bl = strlen(b);
230 if (al == 0 || al != bl || strncmp(a,b,al) != 0)
241 struct plist *add_dir(struct plist *pl, int *np, char *file, char *curr)
243 /* any parent of file that is not a parent of curr
244 * needs to be added to pl
246 int d = common_depth(file, curr);
249 char *c = strchr(file, '/');
251 if (c) l = c-file; else l = strlen(file);
254 while (*file == '/') file++;
255 while (*curr == '/') curr++;
259 if (curr > buf && curr[-1] != '/')
261 while (*file && *file != '/')
263 while (*file == '/') file++;
266 pl = patch_add_file(pl, np, strdup(buf),
272 struct plist *sort_patches(struct plist *pl, int *np)
274 /* sort the patches, add directory names, and re-sort */
280 qsort(pl, *np, sizeof(struct plist), pl_cmp);
284 pl = add_dir(pl, np, pl[i].file, curr);
286 qsort(pl, *np, sizeof(struct plist), pl_cmp);
288 /* array is now stable, so set up parent pointers */
293 for (i=0; i<n; i++) {
294 int d = common_depth(prev, pl[i].file);
298 pl[i].parent = parents[d-1];
299 pl[pl[i].parent].last = i;
301 pl[i].prev = prevnode[d];
303 pl[pl[i].prev].next = i;
312 int get_strip(char *file)
317 while (file && *file) {
318 fd = open(file, O_RDONLY);
324 file = strchr(file, '/');
332 int set_prefix(struct plist *pl, int n, int strip)
335 for(i=0; i<4 && i<n && strip < 0; i++)
336 strip = get_strip(pl[i].file);
339 fprintf(stderr, "%s: Cannot file files to patch: please specify --strip\n",
343 for (i=0; i<n; i++) {
344 char *p = pl[i].file;
346 for (j=0; j<strip; j++) {
347 if (p) p = strchr(p,'/');
348 while (p && *p == '/') p++;
351 fprintf(stderr, "%s: cannot strip %d segments from %s\n",
352 Cmd, strip, pl[i].file);
361 int get_prev(int pos, struct plist *pl, int n)
363 if (pos == -1) return pos;
364 if (pl[pos].prev == -1)
365 return pl[pos].parent;
367 while (pl[pos].open &&
373 int get_next(int pos, struct plist *pl, int n)
375 if (pos == -1) return pos;
382 while (pos >= 0 && pl[pos].next == -1)
383 pos = pl[pos].parent;
389 /* global attributes */
390 int a_delete, a_added, a_common, a_sep, a_void, a_unmatched, a_extra, a_already;
392 void draw_one(int row, struct plist *pl, FILE *f, int reverse)
402 if (pl->calced == 0 && pl->end) {
403 /* better load the patch and count the chunks */
404 struct stream s1, s2;
405 struct stream s = load_segment(f, pl->start, pl->end);
406 struct stream sf = load_file(pl->file);
408 pl->chunks = split_patch(s, &s2, &s1);
410 pl->chunks = split_patch(s, &s1, &s2);
412 if (sf.body == NULL) {
413 pl->wiggles = pl->conflicts = -1;
415 struct file ff, fp1, fp2;
416 struct csl *csl1, *csl2;
418 ff = split_stream(sf, ByWord, 0);
419 fp1 = split_stream(s1, ByWord, 0);
420 fp2 = split_stream(s2, ByWord, 0);
421 csl1 = pdiff(ff, fp1, pl->chunks);
422 csl2 = diff(fp1,fp2);
423 ci = make_merger(ff, fp1, fp2, csl1, csl2, 0, 1);
424 pl->wiggles = ci.wiggles;
425 pl->conflicts = ci.conflicts;
443 else sprintf(hdr, "%2d", pl->chunks);
444 if (pl->wiggles > 99)
445 strcpy(hdr+2, " XX");
446 else sprintf(hdr+2, " %2d", pl->wiggles);
447 if (pl->conflicts > 99)
448 strcpy(hdr+5, " XX ");
449 else sprintf(hdr+5, " %2d ", pl->conflicts);
455 else strcpy(hdr+9, "- ");
457 mvaddstr(row, 0, hdr);
458 mvaddstr(row, 11, pl->file);
462 void addword(struct elmnt e)
464 addnstr(e.start, e.len);
466 void addsep(struct elmnt e1, struct elmnt e2)
471 sscanf(e1.start+1, "%d %d %d", &a, &b, &c);
472 sscanf(e2.start+1, "%d %d %d", &d, &e, &f);
473 sprintf(buf, "@@ -%d,%d +%d,%d @@\n", b,c,e,f);
481 #define CHANGED 32 /* The RESULT is different to ORIG */
482 #define CHANGES 64 /* AFTER is different to BEFORE */
487 struct elmnt prev_elmnt(struct pos *pos, int mode,
488 struct file f1, struct file f2, struct csl *csl)
492 if (pos->a > csl[pos->c].a) {
493 assert(pos->b > csl[pos->c].b);
496 return f1.list[pos->a];
499 if (pos->c) b1 = csl[pos->c-1].b+csl[pos->c-1].len;
504 return f2.list[pos->b];
507 if (pos->c) a1 = csl[pos->c-1].a+csl[pos->c-1].len;
512 return f1.list[pos->a];
514 /* must be time to go to previous common segment */
518 e.start = NULL; e.len = 0;
524 struct elmnt next_elmnt(struct pos *pos, int mode, int *type,
525 struct file f1, struct file f2, struct csl *csl)
529 e.start = NULL; e.len = 0;
535 if (pos->a < csl[pos->c].a) {
537 return f1.list[pos->a++];
542 if (pos->b < csl[pos->c].b) {
544 return f2.list[pos->b++];
549 a1 = csl[pos->c].a + csl[pos->c].len;
552 return f1.list[pos->a++];
554 if (csl[pos->c].len == 0) {
556 e.start = NULL; e.len = 0;
564 void pos_sol(struct pos *pos, int mode, struct file f1, struct file f2, struct csl *csl)
566 /* find the start-of-line before 'pos' considering 'mode' only.
568 if (pos->c < 0) return;
570 struct pos tpos = *pos;
571 struct elmnt e = prev_elmnt(&tpos, mode, f1, f2, csl);
572 if (e.start == NULL) {
575 if (e.start[0] == '\n' || e.start[0] == 0)
581 void prev_pos(struct pos *pos, int mode, struct file f1, struct file f2, struct csl *csl)
583 /* find the start-of-line before 'pos' considering 'mode' only.
586 if (pos->c < 0) return;
588 struct pos tpos = *pos;
589 struct elmnt e = prev_elmnt(&tpos, mode, f1, f2, csl);
590 if (e.start == NULL) {
594 if (e.start[0] == '\n' || e.start[0] == 0) {
600 if (e.start[0] == 0) return;
605 void next_pos(struct pos *pos, int mode, struct file f1, struct file f2, struct csl *csl)
607 /* find the start-of-line after 'pos' considering 'mode' only.
610 if (pos->c < 0) return;
612 struct pos tpos = *pos;
613 struct elmnt e = next_elmnt(&tpos, mode, &type, f1, f2, csl);
614 if (e.start == NULL) {
619 if (e.start[0] == '\n') {
627 void draw_line(int i, struct pos pos, int mode,
628 struct file f1, struct file f2, struct csl *csl, int start, int len)
635 e1 = next_elmnt(&pos, mode, &attr, f1, f2, csl);
636 if (e1.start == NULL) {
640 if (e1.start[0] == '\0') {
643 struct elmnt e2 = f2.list[pos.b-1];
645 (void)attrset(a_sep);
646 sscanf(e1.start+1, "%d %d %d", &a, &b, &c);
647 sscanf(e2.start+1, "%d %d %d", &d, &e, &f);
648 sprintf(buf, "@@ -%d,%d +%d,%d @@\n", b,c, e,f);
655 if (e1.start[0] == '\n') {
658 c = (unsigned char *)e1.start;
661 if (*c >= ' ' && *c != 0x7f) {
662 /* normal width char */
663 if (col >= start && col < len+start)
664 mvaddch(i,col-start, *c);
666 } else if (*c == '\t') {
668 if (col >= start && col < len+start)
669 mvaddch(i, col-start, ' ');
671 } while ((col&7)!= 0);
673 if (col>=start && col < len+start)
674 mvaddch(i, col-start, *c=='\n'?'@':'?');
687 void diff_window(struct plist *p, FILE *f)
689 /* display just the diff, either 'before' or 'after' or all.
691 * In any case highlighting is the same
693 * Current pos is given as a,b,csl where a and b are
694 * before or in the common segment, and one is immediately
696 * The current row is given by 'row'.
697 * Lines do not wrap, be horizontal scrolling is supported.
699 * We don't insist that the top line lies at or above the top
700 * of the screen, as that allows alignment of different views
704 struct stream s1, s2;
714 int mode = BEFORE|AFTER;
716 struct pos pos, tpos;
718 s = load_segment(f, p->start, p->end);
719 ch = split_patch(s, &s1, &s2);
722 f1 = split_stream(s1, ByWord, 0);
723 f2 = split_stream(s2, ByWord, 0);
727 pos.a=0; pos.b=0; pos.c=0;
733 sprintf(buf, "File: %s\n", p->file);
734 attrset(A_BOLD); mvaddstr(0,0,buf); clrtoeol(); attrset(A_NORMAL);
737 if (row < 1 || row >= rows)
741 getmaxyx(stdscr,rows,cols);
751 pos_sol(&tpos, mode, f1, f2, csl);
752 draw_line(row, tpos, mode, f1, f2, csl, start, cols);
753 for (i=row-1; i>=1; i--) {
754 prev_pos(&tpos, mode, f1,f2,csl);
755 draw_line(i, tpos, mode, f1, f2,csl, start,cols);
758 for (i=row+1; i<rows; i++) {
759 next_pos(&tpos, mode, f1, f2, csl);
760 draw_line(i, tpos, mode, f1, f2, csl, start,cols);
766 case 27: /* escape */
780 next_pos(&tpos, mode, f1, f2, csl);
792 prev_pos(&tpos, mode, f1, f2, csl);
814 if (start > 0) start--;
819 if (start < cols) start++;
828 int m; /* merger index */
829 int s; /* stream 0,1,2 for a,b,c */
830 int o; /* offset in that stream */
831 } p, /* the current point */
832 lo, /* eol for start of the current group */
833 hi; /* eol for end of the current group */
834 int side; /* -1 if on the '-' of a diff,
836 * 0 if on an unchanged (lo/hi not meaningful)
839 int same_mpos(struct mpos a, struct mpos b)
841 return a.p.m == b.p.m &&
847 int invalid(int s, enum mergetype type)
851 case Unmatched: return s>0;
852 case Unchanged: return s>0;
853 case Extraneous: return s<2;
854 case Changed: return s==1;
855 case Conflict: return 0;
856 case AlreadyApplied: return 0;
861 struct elmnt next_melmnt(struct mpos *pos,
862 struct file fm, struct file fb, struct file fa,
870 else switch(pos->p.s) {
871 case 0: l = m[pos->p.m].al; break;
872 case 1: l = m[pos->p.m].bl; break;
873 case 2: l = m[pos->p.m].cl; break;
883 } while (invalid(pos->p.s, m[pos->p.m].type));
887 if (pos->p.m == -1 || m[pos->p.m].type == End) {
889 e.start = NULL; e.len = 0;
893 default: /* keep compiler happy */
894 case 0: return fm.list[m[pos->p.m].a + pos->p.o];
895 case 1: return fb.list[m[pos->p.m].b + pos->p.o];
896 case 2: return fa.list[m[pos->p.m].c + pos->p.o];
900 struct elmnt prev_melmnt(struct mpos *pos,
901 struct file fm, struct file fb, struct file fa,
905 while (pos->p.m >=0 && pos->p.o < 0) {
912 } while (pos->p.m >= 0 && invalid(pos->p.s, m[pos->p.m].type));
915 case 0: pos->p.o = m[pos->p.m].al-1; break;
916 case 1: pos->p.o = m[pos->p.m].bl-1; break;
917 case 2: pos->p.o = m[pos->p.m].cl-1; break;
923 e.start = NULL; e.len = 0;
927 default: /* keep compiler happy */
928 case 0: return fm.list[m[pos->p.m].a + pos->p.o];
929 case 1: return fb.list[m[pos->p.m].b + pos->p.o];
930 case 2: return fa.list[m[pos->p.m].c + pos->p.o];
934 int visible(int mode, enum mergetype type, int stream)
938 case 0: /* left - orig plus merge */
940 case End: return A_NORMAL;
941 case Unmatched: return stream == 0 ? a_unmatched : -1;
942 case Unchanged: return stream == 0 ? a_common : -1;
943 case Extraneous: return -1;
944 case Changed: return stream == 0? a_delete:stream==1?-1:a_added;
945 case Conflict: return stream == 0 ? a_unmatched : -1;
946 case AlreadyApplied: return stream == 0 ? a_unmatched : -1;
949 case 1: /* right - old plus new */
951 case End: return A_NORMAL;
952 case Unmatched: return -1;
953 case Unchanged: return stream == 0 ? a_common : -1;
954 case Extraneous: return stream==2 ? a_extra : -1;
955 case Changed: return stream == 0? a_delete:stream==1?-1:a_added;
956 case Conflict: return stream==0?-1:stream==1?(a_delete|A_UNDERLINE):a_added;
957 case AlreadyApplied: return stream==0?-1:stream==1?a_delete:a_added;
962 if (mode == 0) return -1;
963 /* mode can be any combination of ORIG RESULT BEFORE AFTER */
965 case End: /* The END is always visible */
967 case Unmatched: /* Visible in ORIG and RESULT */
968 if (mode & (ORIG|RESULT))
971 case Unchanged: /* visible everywhere, but only show stream 0 */
972 if (stream == 0) return a_common;
974 case Extraneous: /* stream 2 is visible in BEFORE and AFTER */
975 if ((mode & (BEFORE|AFTER))
979 case Changed: /* stream zero visible ORIG and BEFORE, stream 2 elsewhere */
981 (mode & (ORIG|BEFORE)))
984 (mode & (RESULT|AFTER)))
990 if (mode & (ORIG|RESULT))
991 return a_unmatched | A_REVERSE;
995 return a_extra | A_UNDERLINE;
998 if (mode & (AFTER|RESULT))
999 return a_added | A_UNDERLINE;
1003 case AlreadyApplied:
1006 if (mode & (ORIG|RESULT))
1011 return a_delete | A_UNDERLINE;
1015 return a_added | A_UNDERLINE;
1024 int check_line(struct mpos pos, struct file fm, struct file fb, struct file fa,
1025 struct merge *m, int mode);
1027 void next_mline(struct mpos *pos, struct file fm, struct file fb, struct file fa,
1028 struct merge *m, int mode)
1037 if (pos->p.m < 0 || m[pos->p.m].type == End) {
1038 if (pos->side == -1) {
1048 struct elmnt e = next_melmnt(pos, fm,fb,fa,m);
1049 if (e.start == NULL)
1051 if (ends_mline(e) &&
1052 visible(mode, m[pos->p.m].type, pos->p.s) >= 0)
1055 mode2 = check_line(*pos, fm,fb,fa,m,mode);
1057 if (pos->side == 1 && !(mode2 & CHANGES))
1058 /* left a diff-set */
1060 else if (pos->side == -1 && !(mode2 & CHANGES)) {
1061 /*flip from '-' to '+' - need to ensure still visible */
1065 } else if (pos->side == 0 && (mode2 & CHANGES)) {
1066 /* entered a diff-set */
1070 mask = ORIG|RESULT|BEFORE|AFTER|CHANGES|CHANGED;
1072 mask &= ~(ORIG|BEFORE);
1073 if (pos->side == -1)
1074 mask &= ~(RESULT|AFTER);
1075 } while (visible(mode&mask, m[pos->p.m].type, pos->p.s) < 0);
1079 void prev_mline(struct mpos *pos, struct file fm, struct file fb, struct file fa,
1080 struct merge *m, int mode)
1091 struct elmnt e = prev_melmnt(pos, fm,fb,fa,m);
1092 if (e.start == NULL)
1094 if (ends_mline(e) &&
1095 visible(mode, m[pos->p.m].type, pos->p.s) >= 0)
1098 mode2 = check_line(*pos, fm,fb,fa,m,mode);
1100 if (pos->side == -1 && !(mode2 & CHANGES))
1101 /* we have stepped out of a diff-set */
1103 else if (pos->side == 1 && !(mode2 & CHANGES)) {
1104 /* flipped from '+' to '-' */
1108 } else if (pos->side == 0 && (mode2 & CHANGES)) {
1109 /* entered a diffset */
1113 mask = ORIG|RESULT|BEFORE|AFTER|CHANGES|CHANGED;
1115 mask &= ~(ORIG|BEFORE);
1116 if (pos->side == -1)
1117 mask &= ~(RESULT|AFTER);
1118 } while (visible(mode&mask, m[pos->p.m].type, pos->p.s) < 0);
1122 void blank(int row, int start, int cols, int attr)
1124 (void)attrset(attr);
1130 /* Checkline determines how many screen lines are needed to display
1133 * - one line that is before/original
1134 * - one line that is after/result
1135 * - one line that is unchanged/unmatched/extraneous
1136 * - two lines, but only one on the left.
1137 * - two lines, one before and one after.
1138 * CLARIFY/CORRECT this.
1140 int check_line(struct mpos pos, struct file fm, struct file fb, struct file fa,
1141 struct merge *m, int mode)
1146 if (visible(ORIG, m[pos.p.m].type, pos.p.s) >= 0)
1148 if (visible(RESULT, m[pos.p.m].type, pos.p.s) >= 0)
1150 if (visible(BEFORE, m[pos.p.m].type, pos.p.s) >= 0)
1152 if (visible(AFTER, m[pos.p.m].type, pos.p.s) >= 0)
1156 if (m[pos.p.m].type == Changed)
1157 rv |= CHANGED | CHANGES;
1158 else if ((m[pos.p.m].type == AlreadyApplied ||
1159 m[pos.p.m].type == Conflict))
1161 e = prev_melmnt(&pos, fm,fb,fa,m);
1162 } while (e.start != NULL &&
1163 (!ends_mline(e) || visible(mode, m[pos.p.m].type, pos.p.s)==-1));
1165 return rv & (mode | CHANGED | CHANGES);
1168 int mcontains(struct mpos pos,
1169 struct file fm, struct file fb, struct file fa, struct merge *m,
1170 int mode, char *search)
1172 /* See if and of the files, between start of this line and here,
1173 * contain the search string
1176 int len = strlen(search);
1178 e = prev_melmnt(&pos, fm,fb,fa,m);
1181 for (i=0; i<e.len; i++)
1182 if (strncmp(e.start+i, search, len)==0)
1185 } while (e.start != NULL &&
1186 (!ends_mline(e) || visible(mode, m[pos.p.m].type, pos.p.s)==-1));
1190 void draw_mside(int mode, int row, int offset, int start, int cols,
1191 struct file fm, struct file fb, struct file fa, struct merge *m,
1193 int target, int *colp)
1195 /* find previous visible newline, or start of file */
1199 e = prev_melmnt(&pos, fm,fb,fa,m);
1200 } while (e.start != NULL &&
1201 (!ends_mline(e) || visible(mode, m[pos.p.m].type, pos.p.s)==-1));
1206 e = next_melmnt(&pos, fm,fb,fa,m);
1207 if (e.start == NULL ||
1208 (ends_mline(e) && visible(mode, m[pos.p.m].type, pos.p.s) != -1)) {
1209 if (colp) *colp = col;
1210 if (col < start) col = start;
1211 if (e.start && e.start[0] == 0) {
1212 (void)attrset(visible(mode, m[pos.p.m].type, pos.p.s));
1213 mvaddstr(row, col-start+offset, "SEP");
1216 blank(row, col-start+offset, start+cols-col, e.start?visible(mode, m[pos.p.m].type, pos.p.s):A_NORMAL );
1219 if (visible(mode, m[pos.p.m].type, pos.p.s) == -1) {
1222 if (e.start[0] == 0)
1224 (void)attrset(visible(mode, m[pos.p.m].type, pos.p.s));
1225 c = (unsigned char *)e.start;
1228 if (*c >= ' ' && *c != 0x7f) {
1229 if (col >= start && col < start+cols)
1230 mvaddch(row, col-start+offset, *c);
1232 } else if (*c == '\t') {
1234 if (col >= start && col < start+cols)
1235 mvaddch(row, col-start+offset, ' ');
1237 } while ((col&7)!= 0);
1239 if (col >= start && col < start+cols)
1240 mvaddch(row, col-start+offset, '?');
1244 if (colp && target <= col) {
1253 void draw_mline(int row, struct mpos pos,
1254 struct file fm, struct file fb, struct file fa,
1256 int start, int cols, int mode,
1257 int target, int *colp)
1260 * Draw the left and right images of this line
1261 * One side might be a_blank depending on the
1262 * visibility of this newline
1263 * If Changed is found, return ORIG|RESULT | BEFORE|AFTER,
1264 * If AlreadyApplied or Conflict, return BEFORE|AFTER
1267 lcols = (cols-1)/2-1;
1268 rcols = cols - lcols - 3;
1270 attrset(A_STANDOUT);
1271 mvaddch(row, lcols+1, '|');
1274 if (!(mode & CHANGES)) {
1275 mvaddch(row, 0, ' ');
1276 mvaddch(row, lcols+2, ' ');
1277 } else if (mode & (ORIG|BEFORE)) {
1278 mvaddch(row, 0, '-');
1279 mvaddch(row, lcols+2, '-');
1281 mvaddch(row, 0, '+');
1282 mvaddch(row, lcols+2, '+');
1285 if (visible(mode&(ORIG|RESULT), m[pos.p.m].type, pos.p.s) >= 0)
1286 /* visible on left */
1287 draw_mside(mode&(ORIG|RESULT), row, 1, start, lcols,
1288 fm,fb,fa,m, pos, target, colp);
1290 blank(row, 0, lcols+1, a_void);
1291 if (colp) *colp = 0;
1294 if (visible(mode&(BEFORE|AFTER), m[pos.p.m].type, pos.p.s) >= 0)
1295 /* visible on right */
1296 draw_mside(mode&(BEFORE|AFTER), row, lcols+3, start, rcols,
1297 fm,fb,fa,m, pos, 0, NULL);
1299 blank(row, lcols+2, rcols+1, a_void);
1302 extern void cleanlist(struct file a, struct file b, struct csl *list);
1304 void merge_window(struct plist *p, FILE *f, int reverse)
1306 /* display the merge in two side-by-side
1308 * left side shows diff between original and new
1309 * right side shows the requested patch
1311 * Unmatched: c_unmatched - left only
1312 * Unchanged: c_normal - left and right
1313 * Extraneous: c_extra - right only
1314 * Changed-a: c_changed - left and right
1315 * Changed-c: c_new - left and right
1316 * AlreadyApplied-b: c_old - right only
1317 * AlreadyApplied-c: c_applied - left and right
1318 * Conflict-a: ?? left only
1319 * Conflict-b: ?? left and right
1322 * A Conflict is displayed as the original in the
1323 * left side, and the highlighted diff in the right.
1325 * Newlines are the key to display.
1326 * 'pos' is always a newline (or eof).
1327 * For each side that this newline is visible on, we
1328 * rewind the previous newline visible on this side, and
1329 * the display the stuff in between
1331 * A 'position' is an offset in the merger, a stream
1332 * choice (a,b,c - some aren't relevant) and an offset in
1336 struct stream sm, sb, sa, sp; /* main, before, after, patch */
1337 struct file fm, fb, fa;
1338 struct csl *csl1, *csl2;
1345 int mode = ORIG|RESULT | BEFORE|AFTER;
1349 int col=0, target=0;
1351 struct mpos tpos, toppos, botpos;
1354 int meta = 0, tmeta;
1358 int search_notfound = 0;
1360 struct search_anchor {
1361 struct search_anchor *next;
1364 int row, col, searchlen;
1367 sp = load_segment(f, p->start, p->end);
1369 ch = split_patch(sp, &sa, &sb);
1371 ch = split_patch(sp, &sb, &sa);
1373 sm = load_file(p->file);
1374 fm = split_stream(sm, ByWord, 0);
1375 fb = split_stream(sb, ByWord, 0);
1376 fa = split_stream(sa, ByWord, 0);
1378 csl1 = pdiff(fm, fb, ch);
1379 csl2 = diff(fb, fa);
1381 cleanlist(fm, fb, csl1);
1382 cleanlist(fb, fa, csl2);
1385 ci = make_merger(fm, fb, fa, csl1, csl2, 0, 1);
1388 pos.p.m = 0; /* merge node */
1389 pos.p.s = 0; /* stream number */
1390 pos.p.o = -1; /* offset */
1391 next_mline(&pos, fm,fb,fa,ci.merger, mode);
1392 mode2 = check_line(pos, fm,fb,fa, ci.merger, mode);
1393 if (!(mode2 & CHANGES))
1396 if (mode2 & (ORIG|BEFORE))
1404 next_mline(&tpos, fm,fb,fa,ci.merger, mode);
1405 mode2 = check_line(tpos, fm,fb,fa, ci.merger, mode);
1406 } while ((mode2 & CHANGES) && ci.merger[tpos.p.m].type != End);
1412 sprintf(buf, "File: %s%s\n", p->file,reverse?" - reversed":"");
1413 attrset(A_BOLD); mvaddstr(0,0,buf);
1418 if (row < 1 || row >= rows)
1423 getmaxyx(stdscr, rows, cols);
1424 rows--; /* keep last row clear */
1435 /* Always refresh the line */
1436 while (start > target) { start -= 8; refresh = 1;}
1437 if (start < 0) start = 0;
1439 mode2 = check_line(pos, fm,fb,fa,ci.merger,mode);
1440 if ((pos.side <= 0) && (mode2 & (ORIG|BEFORE)))
1441 draw_mline(row,pos,fm,fb,fa,ci.merger,start,cols, mode2&(ORIG|BEFORE|CHANGED|CHANGES), target, &col);
1442 else if (pos.side > 0 && (mode2 & (RESULT|AFTER))) {
1443 if (mode2 & CHANGED)
1444 draw_mline(row,pos,fm,fb,fa,ci.merger,start,cols, mode2&(RESULT|AFTER|CHANGED|CHANGES), target, &col);
1445 else if (mode2 & CHANGES)
1446 draw_mline(row,pos,fm,fb,fa,ci.merger,start,cols, mode2&(AFTER|CHANGED|CHANGES), target, &col);
1448 if (col > (cols-3)/2+start) {
1456 if (start < 0) start = 0;
1463 for (i=row-1; i>=1 && tpos.p.m >= 0; ) {
1464 prev_mline(&tpos, fm,fb,fa,ci.merger, mode);
1465 mode2 = check_line(tpos, fm,fb,fa, ci.merger, mode);
1467 if (tpos.side == 1 &&
1468 (mode2 & (RESULT|AFTER)) &&
1469 (mode2 & (CHANGED)))
1470 draw_mline(i--,tpos,fm,fb,fa,ci.merger,start,cols, mode2&(RESULT|AFTER|CHANGED|CHANGES), 0, NULL);
1471 else if (tpos.side == 1 &&
1472 (mode2 & (RESULT|AFTER)) &&
1473 (mode2 & (CHANGES)))
1474 draw_mline(i--,tpos,fm,fb,fa,ci.merger,start,cols, mode2&(AFTER|CHANGED|CHANGES), 0, NULL);
1475 else if ((tpos.side == 0 || tpos.side == -1) && (mode2 & (ORIG|BEFORE)))
1476 draw_mline(i--,tpos,fm,fb,fa,ci.merger,start,cols, mode2&(ORIG|BEFORE|CHANGED|CHANGES), 0, NULL);
1478 toppos = tpos; toprow = i;
1480 blank(i--, 0, cols, a_void);
1482 for (i=row; i<rows && ci.merger[tpos.p.m].type != End; ) {
1483 mode2 = check_line(tpos, fm,fb,fa,ci.merger,mode);
1484 if ((tpos.side <= 0) && (mode2 & (ORIG|BEFORE)))
1485 draw_mline(i++,tpos,fm,fb,fa,ci.merger,start,cols, mode2&(ORIG|BEFORE|CHANGED|CHANGES), 0, NULL);
1486 else if (tpos.side > 0 && (mode2 & (RESULT|AFTER))) {
1487 if (mode2 & CHANGED)
1488 draw_mline(i++,tpos,fm,fb,fa,ci.merger,start,cols, mode2&(RESULT|AFTER|CHANGED|CHANGES), 0, NULL);
1489 else if (mode2 & CHANGES)
1490 draw_mline(i++,tpos,fm,fb,fa,ci.merger,start,cols, mode2&(AFTER|CHANGED|CHANGES), 0, NULL);
1492 next_mline(&tpos, fm,fb,fa,ci.merger, mode);
1494 botpos = tpos; botrow = i;
1496 blank(i++, 0, cols, a_void);
1499 #define META(c) ((c)|0x1000)
1500 #define SEARCH(c) ((c)|0x2000)
1503 if (num>=0) { char buf[10]; sprintf(buf, "%d ", num); addstr(buf);}
1504 if (meta & META(0)) addstr("ESC...");
1505 if (meta & SEARCH(0)) {
1506 if (searchdir) addstr("Backwards ");
1509 if (search_notfound)
1510 addstr(" - Not Found.");
1511 search_notfound = 0;
1514 move(row,col-start);
1516 tmeta = meta; meta = 0;
1517 tnum = num; num = -1;
1519 case 27: /* escape */
1523 case META('<'): /* start of file */
1528 prev_mline(&tpos, fm,fb,fa,ci.merger,mode);
1529 } while (tpos.p.m >= 0);
1533 case META('>'): /* end of file */
1535 if (tnum >=0) goto start;
1539 next_mline(&tpos, fm,fb,fa,ci.merger,mode);
1540 } while (ci.merger[tpos.p.m].type != End);
1545 if (tnum < 0) tnum = 0;
1546 num = tnum*10 + (c-'0');
1553 /* incr search forward */
1556 search[searchlen] = 0;
1561 /* incr search backwards */
1564 search[searchlen] = 0;
1567 case SEARCH('G'-64):
1568 case SEARCH('S'-64):
1571 tpos = pos; trow = row;
1574 prev_mline(&tpos, fm,fb,fa,ci.merger,mode);
1577 next_mline(&tpos, fm,fb,fa,ci.merger,mode);
1581 case SEARCH('H'-64):
1584 struct search_anchor *a;
1590 struct search_anchor *a;
1596 search_notfound = a->notfound;
1597 searchlen = a->searchlen;
1598 search[searchlen] = 0;
1603 case SEARCH(' ') ... SEARCH('~'):
1606 if (searchlen < sizeof(search)-1)
1607 search[searchlen++] = c & (0x7f);
1608 search[searchlen] = 0;
1609 tpos = pos; trow = row;
1611 search_notfound = 1;
1613 if (mcontains(tpos, fm,fb,fa,ci.merger,mode,search)) {
1616 search_notfound = 0;
1621 prev_mline(&tpos, fm,fb,fa,ci.merger,mode);
1624 next_mline(&tpos, fm,fb,fa,ci.merger,mode);
1626 } while (tpos.p.m >= 0 && ci.merger[tpos.p.m].type != End);
1631 if (toprow >= 1) row -= (toprow+1);
1634 case 'V'-64: /* page down */
1642 case META('v'): /* page up */
1655 if (tnum < 0) tnum = 1;
1656 for (; tnum > 0 ; tnum--) {
1658 next_mline(&tpos, fm,fb,fa,ci.merger, mode);
1659 if (ci.merger[tpos.p.m].type != End) {
1670 next_mline(&tpos, fm,fb,fa,ci.merger, mode);
1671 } while (pos.side != 0 && ci.merger[tpos.p.m].type != End);
1675 next_mline(&tpos, fm,fb,fa,ci.merger, mode);
1676 } while (pos.side == 0 && ci.merger[tpos.p.m].type != End);
1684 prev_mline(&tpos, fm,fb,fa,ci.merger, mode);
1685 } while (tpos.side == 0 && tpos.p.m >= 0);
1689 prev_mline(&tpos, fm,fb,fa,ci.merger, mode);
1690 } while (tpos.side != 0 && tpos.p.m >= 0);
1698 if (tnum < 0) tnum = 1;
1699 for (; tnum > 0 ; tnum--) {
1701 prev_mline(&tpos, fm,fb,fa,ci.merger, mode);
1702 if (tpos.p.m >= 0) {
1713 if (target < 0) target = 0;
1732 case 'a': /* 'after' view in patch window */
1739 case 'b': /* 'before' view in patch window */
1746 case 'o': /* 'original' view in the merge window */
1753 case 'r': /* the 'result' view in the merge window */
1763 if (start > 0) start--;
1768 if (start < cols) start++;
1774 if (meta == SEARCH(0)) {
1775 if (anchor == NULL ||
1776 !same_mpos(anchor->pos, pos) ||
1777 anchor->searchlen != searchlen ||
1778 anchor->col != col) {
1779 struct search_anchor *a = malloc(sizeof(*a));
1783 a->searchlen = searchlen;
1784 a->notfound = search_notfound;
1790 struct search_anchor *a = anchor;
1798 void main_window(struct plist *pl, int n, FILE *f, int reverse)
1800 /* The main window lists all files together with summary information:
1801 * number of chunks, number of wiggles, number of conflicts.
1802 * The list is scrollable
1803 * When a entry is 'selected', we switch to the 'file' window
1804 * The list can be condensed by removing files with no conflict
1805 * or no wiggles, or removing subdirectories
1807 * We record which file in the list is 'current', and which
1808 * screen line it is on. We try to keep things stable while
1811 * Counts are printed before the name using at most 2 digits.
1812 * Numbers greater than 99 are XX
1814 * 27 5 1 drivers/md/md.c
1816 * A directory show the sum in all children.
1819 * select: enter, space, mouseclick
1820 * on file, go to file window
1821 * on directory, toggle open
1822 * up: k, p, control-p uparrow
1823 * Move to previous open object
1824 * down: j, n, control-n, downarrow
1825 * Move to next open object
1828 int pos=0; /* position in file */
1829 int row=1; /* position on screen */
1830 int rows; /* size of screen in rows */
1838 clear(); attrset(0);
1840 mvaddstr(0,0,"Ch Wi Co Patched Files");
1845 if (row <1 || row >= rows)
1849 getmaxyx(stdscr, rows, cols);
1855 for (i=row; i>1; i--) {
1856 tpos = get_prev(tpos, pl, n);
1862 /* Ok, row and pos could be trustworthy now */
1864 for (i=row; i>=1; i--) {
1865 draw_one(i, &pl[tpos], f, reverse);
1866 tpos = get_prev(tpos, pl, n);
1869 for (i=row+1; i<rows; i++) {
1870 tpos = get_next(tpos, pl, n);
1872 draw_one(i, &pl[tpos], f, reverse);
1874 draw_one(i, NULL, f, reverse);
1877 {char bb[20]; sprintf(bb,"%d", c); mvaddstr(0, 70, bb); clrtoeol();}
1886 tpos = get_next(pos, pl, n);
1897 tpos = get_prev(pos, pl, n);
1906 if (pl[pos].end == 0) {
1907 pl[pos].open = ! pl[pos].open;
1910 /* diff_window(&pl[pos], f); */
1911 merge_window(&pl[pos],f,reverse);
1915 case 27: /* escape */
1916 mvaddstr(0,70,"ESC..."); clrtoeol();
1932 int vpatch(int argc, char *argv[], int strip, int reverse, int replace)
1934 /* NOT argv[0] is first arg... */
1941 in = fopen(argv[0], "r");
1943 printf("No %s\n", argv[0]);
1949 if (lseek(fileno(in),0L, 1) == -1) {
1952 fprintf(stderr, "Cannot create a temp file\n");
1957 pl = parse_patch(in, f, &n);
1959 if (set_prefix(pl, n, strip) == 0) {
1960 fprintf(stderr, "%s: aborting\n", Cmd);
1963 pl = sort_patches(pl, &n);
1966 if (fileno(in) == 0) {
1975 for (i=0; i<n ; i++) {
1976 printf("%3d: %3d %2d/%2d %s\n", i, pl[i].parent, pl[i].prev, pl[i].next, pl[i].file);
1980 signal(SIGINT, catch);
1981 signal(SIGQUIT, catch);
1982 signal(SIGTERM, catch);
1983 signal(SIGBUS, catch);
1984 signal(SIGSEGV, catch);
1986 initscr(); cbreak(); noecho();
1988 use_default_colors();
1989 if (!has_colors()) {
1990 a_delete = A_UNDERLINE;
1992 a_common = A_NORMAL;
1994 a_already = A_STANDOUT;
1996 init_pair(1, COLOR_WHITE, COLOR_RED);
1997 a_delete = COLOR_PAIR(1);
1998 init_pair(2, COLOR_WHITE, COLOR_BLUE);
1999 a_added = COLOR_PAIR(2);
2000 a_common = A_NORMAL;
2001 init_pair(3, COLOR_WHITE, COLOR_GREEN);
2002 a_sep = COLOR_PAIR(3);
2003 init_pair(4, COLOR_WHITE, COLOR_YELLOW);
2004 a_void = COLOR_PAIR(4);
2005 init_pair(5, COLOR_BLUE, -1);
2006 a_unmatched = COLOR_PAIR(5);
2007 init_pair(6, COLOR_CYAN, -1);
2008 a_extra = COLOR_PAIR(6);
2010 init_pair(7, COLOR_BLACK, COLOR_CYAN);
2011 a_already = COLOR_PAIR(7);
2013 nonl(); intrflush(stdscr, FALSE); keypad(stdscr, TRUE);
2014 mousemask(ALL_MOUSE_EVENTS, NULL);
2016 main_window(pl, n, in, reverse);
2018 nocbreak();nl();endwin();
2023 WiggleVerbose=1 ~/work/wiggle/wiggle -mR fs/nfsd/nfs4callback.c .patches/removed/144NfsdV4-033 |less
2024 neilb@notabene:/home/src/mm$
2026 ~/work/wiggle/wiggle -BR .patches/removed/144NfsdV4-033